package devcontainer

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"path"
	"strconv"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/templates"
	"code.gitea.io/gitea/services/context"
	devcontainer_service "code.gitea.io/gitea/services/devcontainer"
)

const (
	tplGetDevContainerDetails templates.TplName = "repo/devcontainer/details"
)

// 获取仓库 Dev Container 详细信息
// GET /{username}/{reponame}/devcontainer
func GetDevContainerDetails(ctx *context.Context) {
	if ctx.Doer == nil {
		ctx.HTML(http.StatusForbidden, "")
		return
	}
	var err error
	cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
	log.Info("setting.CustomConf %s", setting.CustomConf)
	log.Info("cfg.Section().Key().Value() %s", cfg.Section("server").Key("ROOT_URL").Value())
	if err != nil {
		ctx.Flash.Error(err.Error(), true)
	}

	ctx.Data["isAdmin"], err = devcontainer_service.IsAdmin(ctx, ctx.Doer, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.Data["HasDevContainer"], err = devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.Data["ValidateDevContainerConfiguration"] = true
	ctx.Data["HasDevContainerConfiguration"], err = devcontainer_service.HasDevContainerConfiguration(ctx, ctx.Repo)
	if err != nil {
		log.Info(err.Error())
		ctx.Data["ValidateDevContainerConfiguration"] = false
		ctx.Flash.Error(err.Error(), true)
	}
	if ctx.Data["HasDevContainerConfiguration"] == false {
		ctx.Data["ValidateDevContainerConfiguration"] = false
	}

	ctx.Data["HasDevContainerDockerfile"], ctx.Data["DockerfilePath"], err = devcontainer_service.HasDevContainerDockerFile(ctx, ctx.Repo)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
	}
	if ctx.Data["HasDevContainer"] == true {
		if ctx.Data["HasDevContainerConfiguration"] == true {
			configurationString, _ := devcontainer_service.GetDevcontainerConfigurationString(ctx, ctx.Repo.Repository)
			configurationModel, _ := devcontainer_service.UnmarshalDevcontainerConfigContent(configurationString)
			imageName := configurationModel.Image
			registry, namespace, repo, tag := devcontainer_service.ParseImageName(imageName)
			log.Info("%v %v", repo, tag)
			ctx.Data["RepositoryAddress"] = registry
			ctx.Data["RepositoryUsername"] = namespace
			ctx.Data["ImageName"] = "dev-" + ctx.Repo.Repository.Name + ":latest"
		}
		if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
			// 获取WebSSH服务端口
			webTerminalURL, err := devcontainer_service.GetWebTerminalURL(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
			if err != nil {
				log.Info(err.Error())
				ctx.Flash.Error(err.Error(), true)
			} else {
				ctx.Data["WebSSHUrl"] = webTerminalURL
			}
		} else {
			webTerminalContainerName := cfg.Section("devcontainer").Key("WEB_TERMINAL_CONTAINER").Value()
			isWebTerminalNotFound, err := devcontainer_service.IsContainerNotFound(ctx, webTerminalContainerName)
			if err != nil {
				ctx.Flash.Error(err.Error(), true)
			}
			var webTerminalStatus string
			if !isWebTerminalNotFound {
				webTerminalStatus, err = devcontainer_service.GetDevContainerStatusFromDocker(ctx, webTerminalContainerName)
				if err != nil {
					ctx.Flash.Error(err.Error(), true)
				}
			}

			if webTerminalContainerName == "" || isWebTerminalNotFound {
				ctx.Flash.Error("webTerminal do not exist. creating ....", true)
				err = devcontainer_service.RegistWebTerminal(ctx)
				if err != nil {
					ctx.Flash.Error(err.Error(), true)
				}
				ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
			} else if webTerminalStatus != "running" && webTerminalStatus != "restarting" {
				err = devcontainer_service.DeleteDevContainerByDocker(ctx, webTerminalContainerName)
				if err != nil {
					ctx.Flash.Error(err.Error(), true)
				}
				err = devcontainer_service.RegistWebTerminal(ctx)
				if err != nil {
					ctx.Flash.Error(err.Error(), true)
				}
				ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
			} else {
				rootPort, err := devcontainer_service.GetPortFromURL(cfg.Section("server").Key("ROOT_URL").Value())
				if err != nil {
					ctx.Flash.Error(err.Error(), true)
				}
				terminalParams := "user=" +
					ctx.Doer.Name +
					"&repo=" +
					ctx.Repo.Repository.Name +
					"&repoid=" +
					strconv.FormatInt(ctx.Repo.Repository.ID, 10) +
					"&userid=" +
					strconv.FormatInt(ctx.Doer.ID, 10) +
					"&domain=" +
					cfg.Section("server").Key("DOMAIN").Value() +
					"&port=" +
					rootPort
				port, err := devcontainer_service.GetMappedPort(ctx, webTerminalContainerName, "7681")
				webTerminalURL, err := devcontainer_service.ReplacePortOfUrl(cfg.Section("server").Key("ROOT_URL").Value(), fmt.Sprintf("%d", port))
				if err != nil {
					log.Info(err.Error())
					ctx.Flash.Error(err.Error(), true)
				}
				ctx.Data["WebSSHUrl"] = webTerminalURL + "?type=docker&" + terminalParams
			}
		}
		terminalURL, err := devcontainer_service.Get_IDE_TerminalURL(ctx, ctx.Doer, ctx.Repo)
		if err == nil {
			ctx.Data["VSCodeUrl"] = "vscode" + terminalURL
			ctx.Data["CursorUrl"] = "cursor" + terminalURL
			ctx.Data["WindsurfUrl"] = "windsurf" + terminalURL
		}
	}
	// 3. 携带数据渲染页面，返回
	ctx.Data["Title"] = ctx.Locale.Tr("repo.dev_container")
	ctx.Data["PageIsDevContainer"] = true
	ctx.Data["Repository"] = ctx.Repo.Repository
	ctx.Data["CreateDevcontainerSettingUrl"] = "/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name + "/devcontainer/createConfiguration"
	ctx.Data["EditDevcontainerConfigurationUrl"] = ctx.Repo.RepoLink + "/_edit/" + ctx.Repo.Repository.DefaultBranch + "/.devcontainer/devcontainer.json"
	ctx.Data["TreeNames"] = []string{".devcontainer", "devcontainer.json"}
	ctx.Data["TreePaths"] = []string{".devcontainer", ".devcontainer/devcontainer.json"}
	ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src"
	ctx.Data["SaveMethods"] = []string{"Container", "DockerFile"}
	ctx.Data["SaveMethod"] = "Container"
	ctx.HTML(http.StatusOK, tplGetDevContainerDetails)
}
func GetDevContainerStatus(ctx *context.Context) {
	// 设置 CORS 响应头
	ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Methods", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Headers", "*")
	var userID string
	if ctx.Doer != nil {
		userID = fmt.Sprintf("%d", ctx.Doer.ID)
	} else {
		query := ctx.Req.URL.Query()
		userID = query.Get("user")
	}
	realTimeStatus, err := devcontainer_service.GetDevContainerStatus(ctx, userID, fmt.Sprintf("%d", ctx.Repo.Repository.ID))
	if err != nil {
		log.Info("%v\n", err)
	}

	ctx.JSON(http.StatusOK, map[string]string{"status": realTimeStatus})
}
func CreateDevContainerConfiguration(ctx *context.Context) {
	hasDevContainerConfiguration, err := devcontainer_service.HasDevContainerConfiguration(ctx, ctx.Repo)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
		return
	}
	if hasDevContainerConfiguration {
		ctx.Flash.Error("Already exist", true)
		ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
		return
	}
	isAdmin, err := devcontainer_service.IsAdmin(ctx, ctx.Doer, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
		return
	}
	if !isAdmin {
		ctx.Flash.Error("permisson denied", true)
		ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
		return
	}
	err = devcontainer_service.CreateDevcontainerConfiguration(ctx.Repo.Repository, ctx.Doer)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
		return
	}
	ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func CreateDevContainer(ctx *context.Context) {
	hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		return
	}
	if hasDevContainer {
		ctx.Flash.Error("Already exist", true)
		return
	}
	err = devcontainer_service.CreateDevcontainerAPIService(ctx, ctx.Repo.Repository, ctx.Doer, []string{}, true)
	if err != nil {
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func DeleteDevContainer(ctx *context.Context) {
	hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		return
	}
	if !hasDevContainer {
		ctx.Flash.Error("Already Deleted.", true)
		return
	}
	err = devcontainer_service.DeleteDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func RestartDevContainer(ctx *context.Context) {
	hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		return
	}
	if !hasDevContainer {
		log.Info(err.Error())
		ctx.Flash.Error("Already delete", true)
		return
	}
	err = devcontainer_service.RestartDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.JSON(http.StatusOK, map[string]string{"status": "6"})
}
func StopDevContainer(ctx *context.Context) {
	hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.Flash.Error(err.Error(), true)
		return
	}
	if !hasDevContainer {
		ctx.Flash.Error("Already delete", true)
		return
	}
	err = devcontainer_service.StopDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		ctx.Flash.Error(err.Error(), true)
	}
	ctx.JSON(http.StatusOK, map[string]string{"status": "7"})
}
func UpdateDevContainer(ctx *context.Context) {
	hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
	if err != nil {
		log.Info(err.Error())
		ctx.JSON(http.StatusOK, map[string]string{"message": err.Error()})
		return
	}
	if !hasDevContainer {
		ctx.JSON(http.StatusOK, map[string]string{"message": "Already delete"})
		return
	}
	// 取得参数
	body, _ := io.ReadAll(ctx.Req.Body)
	var updateInfo devcontainer_service.UpdateInfo
	err = json.Unmarshal(body, &updateInfo)
	if err != nil {
		ctx.JSON(http.StatusOK, map[string]string{"message": err.Error()})
		return
	}
	err = devcontainer_service.UpdateDevContainer(ctx, ctx.Doer, ctx.Repo, &updateInfo)
	if err != nil {
		ctx.JSON(http.StatusOK, map[string]string{"message": err.Error()})
		return
	}
	ctx.JSON(http.StatusOK, map[string]string{"redirect": ctx.Repo.RepoLink + "/devcontainer", "message": "成功"})
}
func GetTerminalCommand(ctx *context.Context) {
	// 设置 CORS 响应头
	ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Methods", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Headers", "*")
	query := ctx.Req.URL.Query()
	cmd, status, err := devcontainer_service.GetTerminalCommand(ctx, query.Get("user"), ctx.Repo.Repository)
	if err != nil {
		log.Info(err.Error())
		status = "error"
	}
	ctx.JSON(http.StatusOK, map[string]string{"command": cmd, "status": status, "workdir": "/workspace/" + ctx.Repo.Repository.Name})
}
func GetDevContainerOutput(ctx *context.Context) {
	// 设置 CORS 响应头
	ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Methods", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Headers", "*")
	query := ctx.Req.URL.Query()
	output, err := devcontainer_service.GetDevContainerOutput(ctx, query.Get("user"), ctx.Repo.Repository)
	if err != nil {
		log.Info(err.Error())
	}
	ctx.JSON(http.StatusOK, map[string]string{"output": output})
}
func SaveDevContainerOutput(ctx *context.Context) {
	// 设置 CORS 响应头
	ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Methods", "*")
	ctx.Resp.Header().Set("Access-Control-Allow-Headers", "*")
	// 处理 OPTIONS 预检请求
	if ctx.Req.Method == "OPTIONS" {
		ctx.JSON(http.StatusOK, "")
		return
	}

	query := ctx.Req.URL.Query()

	// 从请求体中读取输出内容
	body, err := io.ReadAll(ctx.Req.Body)
	if err != nil {
		log.Error("Failed to read request body: %v", err)
		ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Failed to read request body"})
		return
	}
	err = devcontainer_service.SaveDevContainerOutput(ctx, query.Get("user"), ctx.Repo.Repository, string(body))
	if err != nil {
		log.Info(err.Error())
	}
	ctx.JSON(http.StatusOK, "")
}
